/*
	Compresor Gzip
    Copyright (C) 2012 Darío López Padial 
    @bukosabino

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>
*/

/**
 @file codificador.cpp
 @brief Implementación de la Clase Codificador
 @author Dario Lopez Padial
 */

#include "codificador.h"
#include <cassert>

Codificador :: Codificador () {

//Generando la tabla de Distancias
	int aux = 0;
	int aux_2 = 2;
	int aux_3 = 3;
	for (int i=0; i<30; i++){
		tabla_distancia[i].codigo = i;
		if(i<4){
			tabla_distancia[i].num_bits = 0;
			tabla_distancia[i].lon_dis = (i+1);
		}
		else {
			aux++;
			if ( i%2 == 0 ) {
				tabla_distancia[i].num_bits = aux;
				aux--;
				tabla_distancia[i].lon_dis = ( tabla_distancia[i-2].lon_dis + aux_2 );
				aux_2 = ( aux_2 * 2 );
			}
			else{
				tabla_distancia[i].num_bits = aux;				
				tabla_distancia[i].lon_dis = (tabla_distancia[i-2].lon_dis + aux_3);
				aux_3 = ( aux_3 * 2);
			}
		}
	}
	tabla_distancia [30].lon_dis = 32768;

//Generando la tabla de Longitudes
	int aux_codigo = 257;
	int aux_longitud = 3;
	int contador_1 = 0;
	int contador_2 = 1;
	int aux_potencia = 2;
	for ( int j=0; j<28; j++ ) {
		tabla_lon_lit[j].codigo = aux_codigo;
		aux_codigo++;
		if ( j < 8 ) {
			tabla_lon_lit[j].num_bits = contador_1;
			tabla_lon_lit[j].lon_dis = aux_longitud;
			aux_longitud++;
		}
		else {
			if ( j == 8 ) {
				contador_1++;
				tabla_lon_lit[j].num_bits = contador_1;
				tabla_lon_lit[j].lon_dis = tabla_lon_lit[j-1].lon_dis + 1;
			}
			else {
				if ( contador_2 > 4 ) {
					contador_2 = 1;
					contador_1++;
					tabla_lon_lit[j].num_bits = contador_1;
					tabla_lon_lit[j].lon_dis = tabla_lon_lit[j-1].lon_dis + aux_potencia;
					aux_potencia = aux_potencia*2;
				}
				else {
					tabla_lon_lit[j].num_bits = contador_1;
					tabla_lon_lit[j].lon_dis = ( tabla_lon_lit[j-1].lon_dis + aux_potencia );
				}
			}
			contador_2++;
		}
	}
	tabla_lon_lit[28].codigo = 285;		tabla_lon_lit[28].num_bits = 0;		tabla_lon_lit[28].lon_dis = 258;
																																			tabla_lon_lit[29].lon_dis = 300;
//Generando la tabla de Huffman
tabla_huffman [0].codigo = 0;			tabla_huffman [0].num_bits = 8;		tabla_huffman [0].lon_dis = 48;
tabla_huffman [1].codigo = 144;		tabla_huffman [1].num_bits = 9;		tabla_huffman [1].lon_dis = 400;
tabla_huffman [2].codigo = 256;		tabla_huffman [2].num_bits = 7;		tabla_huffman [2].lon_dis = 0;
tabla_huffman [3].codigo = 280;		tabla_huffman [3].num_bits = 8;		tabla_huffman [3].lon_dis = 192;
}


unsigned int Codificador :: longitud_codigo ( unsigned int longitud_aux , VB & bits_auxiliares ) {
	assert ( longitud_aux >=3 && longitud_aux <= 258 );
	unsigned int codigo_auxiliar = 0;
	for ( int i=0; i<29; i++ ) {
		if ( longitud_aux >= tabla_lon_lit [i].lon_dis && longitud_aux <= tabla_lon_lit [i+1].lon_dis ) {
			codigo_auxiliar = tabla_lon_lit [i].codigo;
			bits_auxiliares = VB ( ( longitud_aux - tabla_lon_lit [i].lon_dis ), tabla_lon_lit [i].num_bits );
		}
	}
	return codigo_auxiliar;
}


VB Codificador :: codigo_huffman ( unsigned int codigo_aux ) {
	assert ( codigo_aux >= 0 && codigo_aux <= 287 );
	VB aux;
	if (codigo_aux < 144 ) {
		aux = VB ( codigo_aux + 48 , 8 );
	}
	else {
		if ( codigo_aux > 143 && codigo_aux < 256 ) {
			int auxiliar = ( 256 + codigo_aux );
			aux = VB ( auxiliar, 9 );
		}
		else {
			if ( codigo_aux > 255 && codigo_aux < 280 ) {
				int auxiliar_2 = ( codigo_aux - 256 );
				aux = VB ( auxiliar_2, 7 );
			}
			else{
				if ( codigo_aux > 279 ) {
					int auxiliar_3 = ( codigo_aux - 88 );
					aux = VB ( auxiliar_3, 8 );
				}
			}
		}
	}
	return aux;
}


unsigned int Codificador :: distancia_huffman ( unsigned int distancia_aux, VB & bits_auxiliares ) { 
	assert ( distancia_aux >=1 && distancia_aux <= 32768 );
	int codigo_auxiliar = 0;
	for ( int i=0; i<30; i++ ) {
		if ( distancia_aux >= tabla_distancia [i].lon_dis && distancia_aux <= tabla_distancia [i+1].lon_dis ) {
			codigo_auxiliar = tabla_distancia [i].codigo;
			bits_auxiliares = VB ( ( distancia_aux - tabla_distancia [i].lon_dis ), tabla_distancia [i].num_bits );
		}
	}
	return codigo_auxiliar;
}
